﻿using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

#pragma warning disable CS1591
#pragma warning disable CS8625
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// ReSharper disable ArrangeAccessorOwnerBody

namespace Instrument.Ports
{
    public partial class WindowsApiSerialPort : IDisposable
    {
        // ---------- default values -------------*

        private const int DefaultDataBits = 8;
        private const int MaxDataBits = 8;
        private const int MinDataBits = 5;
        private const Parity DefaultParity = Parity.None;
        private const StopBits DefaultStopBits = StopBits.One;

        //private const Handshake DefaultHandshake = Handshake.None;
        //private const int DefaultBufferSize = 1024;
        private const string DefaultPortName = "COM1";

        private const int DefaultBaudRate = 9600;
        private const bool DefaultDtrEnable = false;
        private const bool DefaultRtsEnable = false;

        private const int DefaultWriteTimeout = 0;
        private const int DefaultReadBufferSize = 4096;
        //private const int DefaultWriteBufferSize = 2048;

        // --------- members supporting exposed properties ------------*
        private string _portName = DefaultPortName;

        private int _baudRate = DefaultBaudRate;
        private byte _dataBits = DefaultDataBits;
        private Parity _parity = DefaultParity;
        private StopBits _stopBits = DefaultStopBits;
        private Encoding _encoding = Encoding.ASCII; // ASCII is default encoding for modem communication, etc.
        private int _maxByteCountForSingleChar = Encoding.ASCII.GetMaxByteCount(1);
        private uint _readTimeout = 0;
        private int _writeTimeout = DefaultWriteTimeout;
        private bool _dtrEnable = DefaultDtrEnable;
        private bool _rtsEnable = DefaultRtsEnable;
        private int _readBufferSize = DefaultReadBufferSize;

        #region 类成员变量

        private SafeFileHandle _handle;
        private bool _disposed = false;

        //private OVERLAPPED _readOverlapped;
        //private OVERLAPPED _writeOverlapped;
        private DCB _dcb;

        #endregion 类成员变量

        #region 属性

        public int BaudRate
        {
            get { return _baudRate; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException(nameof(BaudRate), SR.ArgumentOutOfRange_NeedPosNum);

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(BaudRate)));
                _baudRate = value;
            }
        }

        public byte DataBits
        {
            get => _dataBits;
            set
            {
                if (value < MinDataBits || value > MaxDataBits)
                    throw new ArgumentOutOfRangeException(nameof(DataBits), SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, MinDataBits, MaxDataBits));

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(DataBits)));
                _dataBits = value;
            }
        }

        public bool RtsEnable
        {
            get => _rtsEnable;
            set
            {
                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(RtsEnable)));

                _rtsEnable = value;
            }
        }

        public bool DtrEnable
        {
            get => _dtrEnable;
            set
            {
                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(DtrEnable)));
                _dtrEnable = value;
            }
        }

        public bool IsOpen
        {
            get { return _handle is { IsInvalid: false, IsClosed: false }; }
        }

        public Parity Parity
        {
            get
            {
                return _parity;
            }
            set
            {
                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if (value < Parity.None || value > Parity.Space)
                    throw new ArgumentOutOfRangeException(nameof(Parity), SR.ArgumentOutOfRange_Enum);

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(DataBits)));
                _parity = value;
            }
        }

        public string PortName
        {
            get
            {
                return _portName;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException(nameof(PortName));
                if (value.Length == 0)
                    throw new ArgumentException(SR.PortNameEmpty_String, nameof(PortName));

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(PortName)));
                _portName = value;
            }
        }

        public int ReadBufferSize
        {
            get
            {
                return _readBufferSize;
            }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException(nameof(ReadBufferSize));

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(ReadBufferSize)));

                _readBufferSize = value;
            }
        }

        public uint ReadTimeout
        {
            get
            {
                return _readTimeout;
            }
            set
            {
                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(ReadTimeout)));

                _readTimeout = value;
            }
        }

        public StopBits StopBits
        {
            get
            {
                return _stopBits;
            }
            set
            {
                // this range check looks wrong, but it really is correct. One = 1, Two = 2, and
                // OnePointFive = 3
                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if (value < StopBits.One || value > StopBits.OnePointFive)
                    throw new ArgumentOutOfRangeException(nameof(StopBits), SR.ArgumentOutOfRange_Enum);

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(StopBits)));
                _stopBits = value;
            }
        }

        public int WriteTimeout
        {
            get
            {
                return _writeTimeout;
            }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException(nameof(WriteTimeout), SR.ArgumentOutOfRange_WriteTimeout);

                if (IsOpen)
                    throw new InvalidOperationException(SR.Format(SR.Cant_be_set_when_open, nameof(WriteTimeout)));
                _writeTimeout = value;
            }
        }

        #endregion 属性

        #region 串口资源方法

        private void ThrowLastWin32Error(string message)
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new Win32Exception(errorCode, message);
        }

        //private void InitializeOverlappedStructures()
        //{
        //    _readOverlapped = new OVERLAPPED { hEvent = CreateEvent(IntPtr.Zero, true, false, null) };
        //    _writeOverlapped = new OVERLAPPED { hEvent = CreateEvent(IntPtr.Zero, true, false, null) };

        //    if (_readOverlapped.hEvent == IntPtr.Zero || _writeOverlapped.hEvent == IntPtr.Zero)
        //        ThrowLastWin32Error("创建事件对象失败");
        //}

        private void ConfigureSerialPort()
        {
            _dcb.DCBlength = Marshal.SizeOf<DCB>();

            Debug.Assert(_handle != null, nameof(_handle) + " != null");

            if (!GetCommState(_handle, ref _dcb))
                ThrowLastWin32Error("获取串口状态失败");

            // 配置基本参数
            _dcb.BaudRate = (uint)BaudRate;
            _dcb.ByteSize = DataBits;           // 数据位
            _dcb.Parity = (byte)Parity;      // 无校验
            _dcb.StopBits = (byte)StopBits;  // 1位停止位

            // 配置流控制（禁用硬件流控，启用软件流控）
            _dcb.Flags = DcbFlags.BASIC_CONFIG; // fBinary
            //_dcb.Flags |= 0x0008; // fDtrControl = DTR_CONTROL_ENABLE
            //_dcb.Flags |= 0x0004; // fRtsControl = RTS_CONTROL_ENABLE

            // 双向控制：双方都能暂停对方
            //dcb.Flags |= 0x00000100; // F_OUTX (控制对方发送)
            //dcb.Flags |= 0x00000200; // F_INX  (对方控制我方发送)

            _dcb.XonChar = 0x11;  // XON字符
            _dcb.XoffChar = 0x13; // XOFF字符
            _dcb.XonLim = 1024;   // XON阈值
            _dcb.XoffLim = 4096;   // XOFF阈值

            if (!SetCommState(_handle, ref _dcb))
                ThrowLastWin32Error("设置串口状态失败");
        }

        private void ConfigureTimeouts()
        {
            COMMTIMEOUTS timeouts = new COMMTIMEOUTS
            {
                ReadIntervalTimeout = 10,           // 字符间超时50ms
                ReadTotalTimeoutMultiplier = 0,     // 每字节超时10ms
                ReadTotalTimeoutConstant = ReadTimeout,     // 读取固定超时1s
                WriteTotalTimeoutMultiplier = 10,    // 每字节超时10ms
                WriteTotalTimeoutConstant = 1000     // 写入固定超时1s
            };

            Debug.Assert(_handle != null, nameof(_handle) + " != null");
            if (!SetCommTimeouts(_handle, ref timeouts))
                ThrowLastWin32Error("设置超时参数失败");
        }

        public WindowsApiSerialPort(string portName, int baudRate, Parity parity = Parity.None, byte dataBits = 8, StopBits stopBits = StopBits.One)
        {
            PortName = portName;
            BaudRate = baudRate;
            Parity = parity;
            DataBits = dataBits;
            StopBits = stopBits;
            _dcb = new DCB();
        }

        #endregion 串口资源方法

        public void Open()
        {
            if (IsOpen)
                throw new InvalidOperationException(SR.Port_already_open);

            string fileName = $"\\\\.\\{PortName}";

            // 打开串口设备
            _handle = CreateFile(
                fileName,
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_NONE, // 独占访问
                IntPtr.Zero,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                IntPtr.Zero);

            if (_handle.IsInvalid)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"can not open {PortName}");
            }

            try
            {
                // 配置缓冲区大小
                if (!SetupComm(_handle, 8192, 8192)) // 8KB输入输出缓冲区
                    ThrowLastWin32Error("设置缓冲区大小失败");

                // 配置串口参数
                ConfigureSerialPort();

                // 配置超时设置
                ConfigureTimeouts();

                // 初始化重叠I/O结构
                //InitializeOverlappedStructures();

                Console.WriteLine($"串口 {PortName} 已打开，波特率: {BaudRate}");
            }
            catch
            {
                _handle.Close();
                Close();
                throw;
            }
        }

        public void Close()
        {
            Dispose();
        }

        #region 资源释放

        /// <summary>
        /// 内部关闭方法 - 确保资源正确释放
        /// </summary>
        private void CloseInternal()
        {
            try
            {
                // 1. 首先关闭串口句柄
                if (_handle is { IsInvalid: false, IsClosed: false })
                {
                    _handle.Close();
                    _handle = null;
                }

                // 2. 关闭重叠I/O事件句柄
                //CloseOverlappedHandles();
            }
            catch (Exception ex)
            {
                // 记录错误但不抛出，确保继续清理其他资源
                Debug.WriteLine($"资源释放错误: {ex.Message}");
            }
        }

        ///// <summary>
        ///// 安全关闭重叠I/O事件句柄
        ///// </summary>
        //private void CloseOverlappedHandles()
        //{
        //    SafeCloseHandle(_readOverlapped.hEvent);
        //    SafeCloseHandle(_writeOverlapped.hEvent);

        //    _readOverlapped.hEvent = IntPtr.Zero;
        //    _writeOverlapped.hEvent = IntPtr.Zero;
        //}

        /// <summary>
        /// 安全关闭内核句柄
        /// </summary>
        private static void SafeCloseHandle(IntPtr handle)
        {
            if (handle != IntPtr.Zero)
            {
                CloseHandle(handle);
            }
        }

        public void Dispose()
        {
            Dispose(true);           // 释放托管和非托管资源
            GC.SuppressFinalize(this); // 避免重复调用终结器
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                try
                {
                    // 总是释放非托管资源
                    CloseInternal();

                    if (disposing)
                    {
                        // 释放托管资源
                        DisposeManagedResources();
                    }
                }
                finally
                {
                    _disposed = true;
                }
            }
        }

        /// <summary>
        /// 释放托管资源
        /// </summary>
        protected virtual void DisposeManagedResources()
        {
            // 当前类没有需要释放的托管资源
            // 派生类可以重写此方法
        }

        ~WindowsApiSerialPort()
        {
            Dispose(false);
        }

        #endregion 资源释放

        #region 串口读写

        public byte[] Read()
        {
            if (!IsOpen)
                throw new InvalidOperationException(SR.Port_not_open);

            byte[] buffer = new byte[ReadBufferSize];
#pragma warning disable IDE0018
            // ReSharper disable once TooWideLocalVariableScope
            // ReSharper disable once InlineOutVariableDeclaration
            uint bytesRead = 0;
#pragma warning restore IDE0018

            var readOverlapped = new OVERLAPPED { hEvent = CreateEvent(IntPtr.Zero, true, false, null) };

            try
            {
                if (!ReadFile(_handle, buffer, (uint)buffer.Length, out bytesRead, ref readOverlapped))
                {
                    int error = Marshal.GetLastWin32Error();
                    if (error != ERROR_IO_PENDING)
                        throw new Win32Exception(error, "读取操作失败");

                    // 等待操作完成（带超时）
                    if (!GetOverlappedResult(_handle, ref readOverlapped, out bytesRead, true))
                        throw new Win32Exception(Marshal.GetLastWin32Error(), "读取超时或失败");
                }

                if (bytesRead > 0)
                {
                    byte[] result = new byte[bytesRead];
                    Array.Copy(buffer, result, bytesRead);
                    return result;
                }

#pragma warning disable IDE0301
                return [];
#pragma warning restore IDE0301
            }
            finally
            {
                CloseHandle(readOverlapped.hEvent);
            }
        }

        public void Write(byte[] buffer)
        {
            if (!IsOpen)
                throw new InvalidOperationException(SR.Port_not_open);
            if (buffer == null || buffer.Length == 0)
                throw new ArgumentNullException(nameof(buffer));
            if (buffer.Length == 0)
                throw new ArgumentOutOfRangeException(nameof(buffer));

#pragma warning disable IDE0018
            // ReSharper disable once TooWideLocalVariableScope
            // ReSharper disable once InlineOutVariableDeclaration
            uint bytesWritten = 0;
#pragma warning restore IDE0018
            var writeOverlapped = new OVERLAPPED { hEvent = CreateEvent(IntPtr.Zero, true, false, null) };

            try
            {
                if (!WriteFile(_handle, buffer, (uint)buffer.Length, out bytesWritten, ref writeOverlapped))
                {
                    int error = Marshal.GetLastWin32Error();
                    if (error != ERROR_IO_PENDING)
                        throw new Win32Exception(error, "写入操作失败");

                    if (!GetOverlappedResult(_handle, ref writeOverlapped, out bytesWritten, true))
                        throw new Win32Exception(Marshal.GetLastWin32Error(), "写入失败");
                }

                if (bytesWritten != buffer.Length)
                    throw new IOException($"数据写入不完整: {bytesWritten}/{buffer.Length} 字节");
            }
            finally
            {
                CloseHandle(writeOverlapped.hEvent);
            }
        }

        #endregion 串口读写
    }
}

#pragma warning restore CS1591
#pragma warning restore CS8625